package com.distribute.core.cachedata;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.distribute.core.core.BaseCacheManageCell;
import com.distribute.core.core.BaseNotifyMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 缓存单元 ConcurrentMap
 *
 * @author chennengcheng
 * @date 2021-01-15 11:27
 * @remark
 */
public abstract class BaseConcurrentMapCacheCell<K , V> extends BaseCacheManageCell<BaseNotifyMessage> {

    private static final Logger log = LoggerFactory.getLogger(BaseConcurrentMapCacheCell.class);
    /**
     * 本地缓存
     */
    private ConcurrentMap<K , V> cache = new ConcurrentHashMap<>();

    @Override
    public String getCacheDataJson() {
        return JSONUtil.toJsonStr(cache);
    }

    public V getValueByKey(@Nonnull K key, @Nonnull Supplier<V> getCacheByKeyFun) {
        if (getStatus().get()) {
            return cache.get(key);
        }

        return getCacheByKeyFun.get();
    }


    public V getValueByKey(@Nonnull K key, @Nonnull Supplier<V> getCacheByKeyFun, @Nonnull Function<V, V> resultAndThen) {
        if (getStatus().get()) {
            return resultAndThen.apply(cache.get(key));
        }

        return resultAndThen.apply(getCacheByKeyFun.get());
    }

    public Set<K> getAllKeys(@Nonnull Supplier<Set<K>> getAllKeysFun) {
        if (getStatus().get()) {
            return cache.keySet();
        }

        return getAllKeysFun.get();
    }

    public Collection<V> getAllValues(@Nonnull Supplier<Collection<V>> getAllValuesFun) {
        if (getStatus().get()) {
            return cache.values();
        }

        return getAllValuesFun.get();
    }


    public Collection<V> getAllValues(@Nonnull Supplier<Collection<V>> getAllValuesFun,
                                      @Nonnull Function<Collection<V>, Collection<V>> resultAndThen) {
        if (getStatus().get()) {
            return resultAndThen.apply(cache.values());
        }

        return resultAndThen.apply(getAllValuesFun.get());
    }

    public List<V> listValueByKeys(@Nonnull List<K> keys, @Nonnull Supplier<List<V>> listCacheByKeys) {
        if (CollectionUtil.isEmpty(keys)) {
            return new ArrayList<>();
        }

        if (getStatus().get()) {
            return keys.stream().filter(Objects::nonNull).map(key -> cache.get(key)).filter(Objects::nonNull).collect(Collectors.toList());
        }

        return listCacheByKeys.get();
    }


    public List<V> listValueByKeys(@Nonnull List<K> keys, @Nonnull Supplier<List<V>> listCacheByKeys,
                                   @Nonnull Function<List<V>, List<V>> resultAndThen) {
        if (CollectionUtil.isEmpty(keys)) {
            return new ArrayList<>();
        }

        if (getStatus().get()) {
            List<V> resultValue = keys.stream().filter(Objects::nonNull).map(key -> cache.get(key)).filter(Objects::nonNull).collect(Collectors.toList());
            return resultAndThen.apply(resultValue);
        }

        return resultAndThen.apply(listCacheByKeys.get());
    }

    public boolean containsKey(@Nonnull K key, @Nonnull Supplier<Boolean> containsKeyFun) {
        if (getStatus().get()) {
            return cache.containsKey(key);
        }

        return containsKeyFun.get();
    }

    public ConcurrentMap<K , V> getCache(){
        return cache;
    }

    public abstract int flushData(BaseNotifyMessage notifyMessage, Map<K , V> cache);

    public abstract int flushAllData(Map<K , V> cache);

    public abstract int initCacheData(Map<K , V> cache);

    @Override
    protected int flush(BaseNotifyMessage notifyMessage) {
        log.info("收到缓存刷新消息：" + JSONUtil.toJsonStr(notifyMessage));
        return flushData(notifyMessage, cache);
    }

    @Override
    protected int flushAll() {
        log.info("收到全量缓存刷新消息");
        return flushAllData(cache);
    }

    @Override
    public int initCache() {
        return initCacheData(cache);
    }

    @Override
    public int size() {
        return this.cache.size();
    }
}
